package com.example.opengldemo.buffer;

import android.opengl.GLES30;

import com.example.opengldemo.util.MatrixState;
import com.example.opengldemo.util.ShaderBuffer;
import com.example.opengldemo.util.ShaderHandle;
import com.example.opengldemo.util.ShaderUtil;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;

public class Ball {


    String mVertexShader;
    String mFragmentShader;

    float xAngle = 0;
    float yAngle = 0;
    float zAngle = 0;

    float bHalf = 0;
    float r = 0;

    public Ball(MySurfaceView mv, float scale, float aHalf, int n) {
        initVertexData(scale, aHalf, n);

        initShader(mv);
        //initVAO();
    }



    int vertexBufferId ;
    int normalBufferId ;
    int texcoorsBufferId ;
    int vaoId ;
    public void initVertexData(float scale, float aHalf, int n) {
        int[] bufferId = new int[3];
        GLES30.glGenBuffers(3 , bufferId , 0);
        vertexBufferId = bufferId[0];
        normalBufferId = bufferId[1];
        texcoorsBufferId = bufferId[2];


        aHalf *= scale;
        shaderBuffer = new ShaderBuffer() ;
        bHalf = aHalf * 0.618034f;
        r = (float) Math.sqrt(aHalf * aHalf + bHalf * bHalf);
        ArrayList<Float> alVertix20 = new ArrayList<Float>();
        ArrayList<Integer> alFaceIndex20 = new ArrayList<Integer>();
        alVertix20.add(0f);
        alVertix20.add(aHalf);
        alVertix20.add(-bHalf);

        alVertix20.add(0f);
        alVertix20.add(aHalf);
        alVertix20.add(bHalf);
        alVertix20.add(aHalf);
        alVertix20.add(bHalf);
        alVertix20.add(0f);
        alVertix20.add(bHalf);
        alVertix20.add(0f);
        alVertix20.add(-aHalf);
        alVertix20.add(-bHalf);
        alVertix20.add(0f);
        alVertix20.add(-aHalf);
        alVertix20.add(-aHalf);
        alVertix20.add(bHalf);
        alVertix20.add(0f);

        alVertix20.add(-bHalf);
        alVertix20.add(0f);
        alVertix20.add(aHalf);
        alVertix20.add(bHalf);
        alVertix20.add(0f);
        alVertix20.add(aHalf);
        alVertix20.add(aHalf);
        alVertix20.add(-bHalf);
        alVertix20.add(0f);
        alVertix20.add(0f);
        alVertix20.add(-aHalf);
        alVertix20.add(-bHalf);
        alVertix20.add(-aHalf);
        alVertix20.add(-bHalf);
        alVertix20.add(0f);

        alVertix20.add(0f);
        alVertix20.add(-aHalf);
        alVertix20.add(bHalf);
        alFaceIndex20.add(0);
        alFaceIndex20.add(1);
        alFaceIndex20.add(2);
        alFaceIndex20.add(0);
        alFaceIndex20.add(2);
        alFaceIndex20.add(3);
        alFaceIndex20.add(0);
        alFaceIndex20.add(3);
        alFaceIndex20.add(4);
        alFaceIndex20.add(0);
        alFaceIndex20.add(4);
        alFaceIndex20.add(5);
        alFaceIndex20.add(0);
        alFaceIndex20.add(5);
        alFaceIndex20.add(1);

        alFaceIndex20.add(1);
        alFaceIndex20.add(6);
        alFaceIndex20.add(7);
        alFaceIndex20.add(1);
        alFaceIndex20.add(7);
        alFaceIndex20.add(2);
        alFaceIndex20.add(2);
        alFaceIndex20.add(7);
        alFaceIndex20.add(8);
        alFaceIndex20.add(2);
        alFaceIndex20.add(8);
        alFaceIndex20.add(3);
        alFaceIndex20.add(3);
        alFaceIndex20.add(8);
        alFaceIndex20.add(9);
        alFaceIndex20.add(3);
        alFaceIndex20.add(9);
        alFaceIndex20.add(4);
        alFaceIndex20.add(4);
        alFaceIndex20.add(9);
        alFaceIndex20.add(10);
        alFaceIndex20.add(4);
        alFaceIndex20.add(10);
        alFaceIndex20.add(5);
        alFaceIndex20.add(5);
        alFaceIndex20.add(10);
        alFaceIndex20.add(6);
        alFaceIndex20.add(5);
        alFaceIndex20.add(6);
        alFaceIndex20.add(1);

        alFaceIndex20.add(6);
        alFaceIndex20.add(11);
        alFaceIndex20.add(7);
        alFaceIndex20.add(7);
        alFaceIndex20.add(11);
        alFaceIndex20.add(8);
        alFaceIndex20.add(8);
        alFaceIndex20.add(11);
        alFaceIndex20.add(9);
        alFaceIndex20.add(9);
        alFaceIndex20.add(11);
        alFaceIndex20.add(10);
        alFaceIndex20.add(10);
        alFaceIndex20.add(11);
        alFaceIndex20.add(6);
        float[] vertices20 = VectorUtil.calVertices(alVertix20, alFaceIndex20);

        ArrayList<Float> alVertix = new ArrayList<Float>();
        ArrayList<Integer> alFaceIndex = new ArrayList<Integer>();
        int vnCount = 0;
        for (int k = 0; k < vertices20.length; k += 9) {
            float[] v1 = new float[]{vertices20[k + 0], vertices20[k + 1], vertices20[k + 2]};
            float[] v2 = new float[]{vertices20[k + 3], vertices20[k + 4], vertices20[k + 5]};
            float[] v3 = new float[]{vertices20[k + 6], vertices20[k + 7], vertices20[k + 8]};

            for (int i = 0; i <= n; i++) {
                float[] viStart = VectorUtil.devideBall(r, v1, v2, n, i);
                float[] viEnd = VectorUtil.devideBall(r, v1, v3, n, i);
                for (int j = 0; j <= i; j++) {
                    float[] vi = VectorUtil.devideBall(r, viStart, viEnd, i, j);
                    alVertix.add(vi[0]);
                    alVertix.add(vi[1]);
                    alVertix.add(vi[2]);
                }
            }

            for (int i = 0; i < n; i++) {
                if (i == 0) {
                    alFaceIndex.add(vnCount + 0);
                    alFaceIndex.add(vnCount + 1);
                    alFaceIndex.add(vnCount + 2);
                    vnCount += 1;
                    if (i == n - 1) {
                        vnCount += 2;
                    }
                    continue;
                }
                int iStart = vnCount;
                int viCount = i + 1;
                int iEnd = iStart + viCount - 1;

                int iStartNext = iStart + viCount;
                int viCountNext = viCount + 1;
                int iEndNext = iStartNext + viCountNext - 1;

                for (int j = 0; j < viCount - 1; j++) {
                    int index0 = iStart + j;
                    int index1 = index0 + 1;
                    int index2 = iStartNext + j;
                    int index3 = index2 + 1;
                    alFaceIndex.add(index0);
                    alFaceIndex.add(index2);
                    alFaceIndex.add(index3);
                    alFaceIndex.add(index0);
                    alFaceIndex.add(index3);
                    alFaceIndex.add(index1);
                }// j
                alFaceIndex.add(iEnd);
                alFaceIndex.add(iEndNext - 1);
                alFaceIndex.add(iEndNext);
                vnCount += viCount;
                if (i == n - 1) {
                    vnCount += viCountNext;
                }
            }
        }


        float[] vertices = VectorUtil.calVertices(alVertix, alFaceIndex);
        float[] normals = vertices;
        shaderBuffer.setVertexBuffer(vertices);
        shaderBuffer.setNormalBuffer(normals);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER  , vertexBufferId);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER , vertices.length*4 ,
                shaderBuffer.getVertexBuffer() ,  GLES30.GL_STATIC_DRAW);

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER  , normalBufferId);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER , normals.length*4 ,
                shaderBuffer.getNormalBuffer() ,  GLES30.GL_STATIC_DRAW);

        float[] colors = new float[shaderBuffer.vCount * 4];
        int Count = 0;
        for (int i = 0; i < shaderBuffer.vCount; i++) {
            colors[Count++] = 1;    //r
            colors[Count++] = 1;    //g
            colors[Count++] = 1;    //b
            colors[Count++] = 1;    //a

        }
        shaderBuffer.setColorBuffer(colors);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER  , texcoorsBufferId);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER , colors.length*4 ,
                shaderBuffer.getColorBuffer() ,  GLES30.GL_STATIC_DRAW);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER  , 0);

    }
    ShaderHandle shaderHandle ;
    ShaderBuffer shaderBuffer ;
    public void initShader(MySurfaceView mv) {
        mVertexShader = ShaderUtil.loadFromAssetsFile("simple_13_02/vertex_color_light.sh", mv.getResources());
        mFragmentShader = ShaderUtil.loadFromAssetsFile("simple_13_02/frag_color_light.sh", mv.getResources());
        shaderHandle = new ShaderHandle(ShaderUtil.createProgram(mVertexShader, mFragmentShader) );
    }

    public void drawSelf() {
        MatrixState.rotate(xAngle, 1, 0, 0);
        MatrixState.rotate(yAngle, 0, 1, 0);
        MatrixState.rotate(zAngle, 0, 0, 1);
        shaderHandle.program()
                .bindMMaxtrixHandle()
                .bindMVPMaxtrixHandle()
                .bindCameraHandle()
                .bindLightHandle();
//                .bindVertexHandle(shaderBuffer.getVertexBuffer())
//                .bindNormalHandle(shaderBuffer.getNormalBuffer())
//                .bindColorsHandle(shaderBuffer.getColorBuffer()) ;
        GLES30.glBindVertexArray(vaoId);
        GLES30.glLineWidth(2);
        shaderHandle.run(GLES30.GL_LINE_STRIP ,shaderBuffer.vCount);
        GLES30.glBindVertexArray(0);
    }


    public void drawSelf3() {
        MatrixState.rotate(xAngle, 1, 0, 0);
        MatrixState.rotate(yAngle, 0, 1, 0);
        MatrixState.rotate(zAngle, 0, 0, 1);
        shaderHandle.program()
                .bindMMaxtrixHandle()
                .bindMVPMaxtrixHandle()
                .bindCameraHandle()
                .bindLightHandle();
//                .bindVertexHandle(shaderBuffer.getVertexBuffer())
//                .bindNormalHandle(shaderBuffer.getNormalBuffer())
//                .bindColorsHandle(shaderBuffer.getColorBuffer()) ;
        GLES30.glBindVertexArray(vaoId);
        GLES30.glLineWidth(2);
        shaderHandle.run(GLES30.GL_LINE_STRIP ,shaderBuffer.vCount);
        GLES30.glBindVertexArray(0);
    }

    public void initVertexData2(float scale, float aHalf, int n) {
        int[] bufferId = new int[3];
        GLES30.glGenBuffers(3 , bufferId , 0);
        vertexBufferId = bufferId[0];
        normalBufferId = bufferId[1];
        texcoorsBufferId = bufferId[2];


        aHalf *= scale;
        shaderBuffer = new ShaderBuffer() ;
        bHalf = aHalf * 0.618034f;
        r = (float) Math.sqrt(aHalf * aHalf + bHalf * bHalf);
        ArrayList<Float> alVertix20 = new ArrayList<Float>();
        ArrayList<Integer> alFaceIndex20 = new ArrayList<Integer>();
        alVertix20.add(0f);alVertix20.add(aHalf);alVertix20.add(-bHalf);

        alVertix20.add(0f);alVertix20.add(aHalf);alVertix20.add(bHalf);
        alVertix20.add(aHalf);alVertix20.add(bHalf);alVertix20.add(0f);
        alVertix20.add(bHalf);alVertix20.add(0f);alVertix20.add(-aHalf);
        alVertix20.add(-bHalf);alVertix20.add(0f);alVertix20.add(-aHalf);
        alVertix20.add(-aHalf);alVertix20.add(bHalf);alVertix20.add(0f);

        alVertix20.add(-bHalf);alVertix20.add(0f);alVertix20.add(aHalf);
        alVertix20.add(bHalf);alVertix20.add(0f);alVertix20.add(aHalf);
        alVertix20.add(aHalf);alVertix20.add(-bHalf);alVertix20.add(0f);
        alVertix20.add(0f);alVertix20.add(-aHalf);alVertix20.add(-bHalf);
        alVertix20.add(-aHalf);alVertix20.add(-bHalf);alVertix20.add(0f);

        alVertix20.add(0f);alVertix20.add(-aHalf);alVertix20.add(bHalf);
        alFaceIndex20.add(0);alFaceIndex20.add(1);alFaceIndex20.add(2);
        alFaceIndex20.add(0);alFaceIndex20.add(2);alFaceIndex20.add(3);
        alFaceIndex20.add(0);alFaceIndex20.add(3);alFaceIndex20.add(4);
        alFaceIndex20.add(0);alFaceIndex20.add(4);alFaceIndex20.add(5);
        alFaceIndex20.add(0);alFaceIndex20.add(5);alFaceIndex20.add(1);

        alFaceIndex20.add(1);alFaceIndex20.add(6);alFaceIndex20.add(7);
        alFaceIndex20.add(1);alFaceIndex20.add(7);alFaceIndex20.add(2);
        alFaceIndex20.add(2);alFaceIndex20.add(7);alFaceIndex20.add(8);
        alFaceIndex20.add(2);alFaceIndex20.add(8);alFaceIndex20.add(3);
        alFaceIndex20.add(3);alFaceIndex20.add(8);alFaceIndex20.add(9);
        alFaceIndex20.add(3);alFaceIndex20.add(9);alFaceIndex20.add(4);
        alFaceIndex20.add(4);alFaceIndex20.add(9);alFaceIndex20.add(10);
        alFaceIndex20.add(4);alFaceIndex20.add(10);alFaceIndex20.add(5);
        alFaceIndex20.add(5);alFaceIndex20.add(10);alFaceIndex20.add(6);
        alFaceIndex20.add(5);alFaceIndex20.add(6);alFaceIndex20.add(1);

        alFaceIndex20.add(6);alFaceIndex20.add(11);alFaceIndex20.add(7);
        alFaceIndex20.add(7);alFaceIndex20.add(11);alFaceIndex20.add(8);
        alFaceIndex20.add(8);alFaceIndex20.add(11);alFaceIndex20.add(9);
        alFaceIndex20.add(9);alFaceIndex20.add(11);alFaceIndex20.add(10);
        alFaceIndex20.add(10);alFaceIndex20.add(11);alFaceIndex20.add(6);
        float[] vertices20 = VectorUtil.calVertices(alVertix20, alFaceIndex20);

        ArrayList<Float> alVertix = new ArrayList<Float>();
        ArrayList<Integer> alFaceIndex = new ArrayList<Integer>();
        int vnCount = 0;
        for (int k = 0; k < vertices20.length; k += 9) {
            float[] v1 = new float[]{vertices20[k + 0], vertices20[k + 1], vertices20[k + 2]};
            float[] v2 = new float[]{vertices20[k + 3], vertices20[k + 4], vertices20[k + 5]};
            float[] v3 = new float[]{vertices20[k + 6], vertices20[k + 7], vertices20[k + 8]};

            for (int i = 0; i <= n; i++) {
                float[] viStart = VectorUtil.devideBall(r, v1, v2, n, i);
                float[] viEnd = VectorUtil.devideBall(r, v1, v3, n, i);
                for (int j = 0; j <= i; j++) {
                    float[] vi = VectorUtil.devideBall(r, viStart, viEnd, i, j);
                    alVertix.add(vi[0]);
                    alVertix.add(vi[1]);
                    alVertix.add(vi[2]);
                }
            }

            for (int i = 0; i < n; i++) {
                if (i == 0) {
                    alFaceIndex.add(vnCount + 0);
                    alFaceIndex.add(vnCount + 1);
                    alFaceIndex.add(vnCount + 2);
                    vnCount += 1;
                    if (i == n - 1) {
                        vnCount += 2;
                    }
                    continue;
                }
                int iStart = vnCount;
                int viCount = i + 1;
                int iEnd = iStart + viCount - 1;

                int iStartNext = iStart + viCount;
                int viCountNext = viCount + 1;
                int iEndNext = iStartNext + viCountNext - 1;

                for (int j = 0; j < viCount - 1; j++) {
                    int index0 = iStart + j;
                    int index1 = index0 + 1;
                    int index2 = iStartNext + j;
                    int index3 = index2 + 1;
                    alFaceIndex.add(index0);
                    alFaceIndex.add(index2);
                    alFaceIndex.add(index3);
                    alFaceIndex.add(index0);
                    alFaceIndex.add(index3);
                    alFaceIndex.add(index1);
                }// j
                alFaceIndex.add(iEnd);
                alFaceIndex.add(iEndNext - 1);
                alFaceIndex.add(iEndNext);
                vnCount += viCount;
                if (i == n - 1) {
                    vnCount += viCountNext;
                }
            }
        }
        float[] vertices = VectorUtil.calVertices(alVertix, alFaceIndex);
        float[] normals = vertices;

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER  , vertexBufferId);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER , vertices.length*4 ,
                null ,  GLES30.GL_STATIC_DRAW);
        ByteBuffer byteBuffer = (ByteBuffer)GLES30.glMapBufferRange(
                        GLES30.GL_ARRAY_BUFFER, //缓冲类型
                        0, //偏移量
                        vertices.length*4, //长度(以字节计)
                        GLES30.GL_MAP_WRITE_BIT|GLES30.GL_MAP_INVALIDATE_BUFFER_BIT);//访问标志
        if(byteBuffer==null){return;}
        byteBuffer.order(ByteOrder.nativeOrder());
        shaderBuffer.setVertexBuffer((FloatBuffer) (byteBuffer.asFloatBuffer().put(vertices).position(0)));
        if(GLES30.glUnmapBuffer(GLES30.GL_ARRAY_BUFFER)==false){return;}

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER  , normalBufferId);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER , normals.length*4 ,
                null ,  GLES30.GL_STATIC_DRAW);
        byteBuffer = (ByteBuffer)GLES30.glMapBufferRange(
                GLES30.GL_ARRAY_BUFFER, //缓冲类型
                0, //偏移量
                normals.length*4, //长度(以字节计)
                GLES30.GL_MAP_WRITE_BIT|GLES30.GL_MAP_INVALIDATE_BUFFER_BIT);//访问标志
        if(byteBuffer==null){return;}
        byteBuffer.order(ByteOrder.nativeOrder());
        shaderBuffer.setNormalBuffer((FloatBuffer) (byteBuffer.asFloatBuffer().put(normals).position(0)));
        if(GLES30.glUnmapBuffer(GLES30.GL_ARRAY_BUFFER)==false){return;}


        float[] colors = new float[shaderBuffer.vCount * 4];
        int Count = 0;
        for (int i = 0; i < shaderBuffer.vCount; i++) {
            colors[Count++] = 1;    //r
            colors[Count++] = 1;    //g
            colors[Count++] = 1;    //b
            colors[Count++] = 1;    //a

        }

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER  , texcoorsBufferId);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER , colors.length*4 ,
                null ,  GLES30.GL_STATIC_DRAW);
        byteBuffer = (ByteBuffer)GLES30.glMapBufferRange(
                GLES30.GL_ARRAY_BUFFER, //缓冲类型
                0, //偏移量
                colors.length*4, //长度(以字节计)
                GLES30.GL_MAP_WRITE_BIT|GLES30.GL_MAP_INVALIDATE_BUFFER_BIT);//访问标志
        if(byteBuffer==null){return;}
        byteBuffer.order(ByteOrder.nativeOrder());
        shaderBuffer.setColorBuffer((FloatBuffer) (byteBuffer.asFloatBuffer().put(colors).position(0)));
        if(GLES30.glUnmapBuffer(GLES30.GL_ARRAY_BUFFER)==false){return;}

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER  , 0);

    }

    public void drawSelf2() {
        MatrixState.rotate(xAngle, 1, 0, 0);
        MatrixState.rotate(yAngle, 0, 1, 0);
        MatrixState.rotate(zAngle, 0, 0, 1);
        shaderHandle.program()
                .bindMMaxtrixHandle()
                .bindMVPMaxtrixHandle()
                .bindCameraHandle()
                .bindLightHandle();
//                .bindVertexHandle(shaderBuffer.getVertexBuffer())
//                .bindNormalHandle(shaderBuffer.getNormalBuffer())
//                .bindColorsHandle(shaderBuffer.getColorBuffer()) ;
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER , vertexBufferId);
        GLES30.glVertexAttribPointer(shaderHandle.get_A_PositionHandle(),3, GLES30.GL_FLOAT, false,3*4,0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER , normalBufferId);
        GLES30.glVertexAttribPointer(shaderHandle.get_A_NormalHandle(),3, GLES30.GL_FLOAT, false,3*4,0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER , texcoorsBufferId);
        GLES30.glVertexAttribPointer(shaderHandle.get_A_ColorHandle(),4, GLES30.GL_FLOAT, false,4*4,0);
        GLES30.glLineWidth(2);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,0);
        shaderHandle.run(GLES30.GL_LINE_STRIP ,shaderBuffer.vCount);
    }


    private void initVAO() {
        int[] vaoIds=new int[1];
        GLES30.glGenVertexArrays(1 , vaoIds , 0);
        vaoId = vaoIds[0] ;
        GLES30.glBindVertexArray(vaoId);
        GLES30.glEnableVertexAttribArray(shaderHandle.get_A_PositionHandle());
        GLES30.glEnableVertexAttribArray(shaderHandle.get_A_NormalHandle());
        GLES30.glEnableVertexAttribArray(shaderHandle.get_A_ColorHandle());
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER , vertexBufferId);
        GLES30.glVertexAttribPointer(shaderHandle.get_A_PositionHandle(),3, GLES30.GL_FLOAT, false,3*4,0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER , normalBufferId);
        GLES30.glVertexAttribPointer(shaderHandle.get_A_NormalHandle(),3, GLES30.GL_FLOAT, false,3*4,0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER , texcoorsBufferId);
        GLES30.glVertexAttribPointer(shaderHandle.get_A_ColorHandle(),4, GLES30.GL_FLOAT, false,4*4,0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,0);
        GLES30.glBindVertexArray(0);
    }
}
